package chapter_07;

// 进一步研究方法和类

/*
1. 方法重载
同一个类中的2个或2个以上的方法可以有同一个名字，只要它们的参数声明不同即可。在这
种情况下，该方法就被称为重载，这个过程称为方法重载。
当一个重载方法被调用时，Java用参数的类型和（或）数量来表明实际调用的重载方
法的版本。因此，每个重载方法的参数的类型和（或）数量必须是不同的。虽然每个重载
方法可以有不同的返回类型，但返回类型并不足以区分所使用的是哪个方法。当Java调用
一个重载方法时，参数与调用参数匹配的方法被执行。
    A. 构造函数重载
    略。

2. 把对象作为参数
略。

3. 参数是如何传递的
总的来说，计算机语言给子程序传递参数的方法有两种。第一种方法是按值传递
（call-by-value）。这种方法将一个参数值（value）复制成为子程序的正式参数。这样，对
子程序的参数的改变不影响调用它的参数。第二种传递参数的方法是引用调用
（call-by-reference）。在这种方法中，参数的引用（而不是参数值）被传递给子程序参数。
在子程序中，该引用用来访问调用中指定的实际参数。这样，对子程序参数的改变将会影
响调用子程序的参数。你将看到，根据传递的对象不同，Java将使用这两种不同的方法。
在Java中，当你给方法传递一个简单类型时，它是按值传递的。
当你给方法传递一个对象时，这种情形就会发生戏剧性的变化，因为对象是通过引用传递的。
当一个对象引用被传递给方法时，引用本身使用按值调用被传递。但是，因为被传递的值指向一个对象，
该值的拷贝仍然指向它相应的参数所指向的同一个对象。

4. 返回对象
略。

5. 递归
当一个方法调用它自身的时候，堆栈就会给新的局部变量和自变量分配内存，方法代码就
带着这些新的变量从头执行。递归调用并不产生方法新的拷贝。只有参数是新的。每当递
归调用返回时，旧的局部变量和自变量就从堆栈中清除，运行从方法中的调用点重新开始。
递归方法可以说是像“望远镜”一样，可以自由伸缩。

6. 介绍访问控制
Java的访问指示符有public（公共的，全局的）、private（私有的，局部的）、和
protected（受保护的）。Java也定义了一个默认访问级别。指示符protected仅用于继
承情况中。
让我们从定义public和private开始。当一个类成员被public指示符修饰时，该成员可以
被你的程序中的任何其他代码访问。当一个类成员被指定为private时，该成员只能被它的
类中的其他成员访问。

7. 理解static
如果一个成员被声明为static，它就能够在它的类的任何对象创建之前被访问，而不必引用
任何对象。你可以将方法和变量都声明为static。static成员的最常见的例子是main( )。因为
在程序开始执行时必须调用main()，所以它被声明为static。
声明为static的变量实质上就是全局变量。当声明一个对象时，并不产生static变量的拷
贝，而是该类所有的实例变量共用同一个static变量。
声明为static的方法有以下几条限制：
· 它们仅能调用其他的static方法。
· 它们只能访问static数据。
· 它们不能以任何方式引用this或super。

8. 介绍final
一个变量可以声明为final，这样做的目的是阻止它的内容被修改。这意味着在声明final变量
的时候，你必须初始化它。
为final变量的所有的字符选择大写是一个普遍的编码约定。声明为final的变量在实例中不占
用内存。这样，一个final变量实质上是一个常数。

9. 重新温习数组
数组是作为对象来实现的。

10. 介绍嵌套类和内部类
在另一个类中定义的类就是嵌套类（nested classes）。嵌套类的范围由装入它的类的范
围限制。这样，如果类B被定义在类A之内，那么B为A所知，然而不被A的外面所知。嵌套
类可以访问嵌套它的类的成员，包括private成员。但是，包围类不能访问嵌套类的成员。
嵌套类一般有2种类型：前面加static标识符的和不加static标识符的。一个static的嵌套
类有static修饰符。因为它是static，所以只能通过对象来访问它包围类的成员。也就是说，
它不能直接引用它包围类的成员。因为有这个限制，所以static嵌套类很少使用。
嵌套类最重要的类型是内部类（inner class）。内部类是非static的嵌套类。它可以访问
它的外部类的所有变量和方法，它可以直接引用它们，就像外部类中的其他非static成员的
功能一样。这样，一个内部类完全在它的包围类的范围之内。

11. 探索String类
字符串的另一个特点是，String类型的对象是不可改变的；一旦创建了一个字符串对象，
它的内容是不能被改变的。这看起来是一个严格的限制，但实际上不是，因为这有两个原
因：
· 如果你需要改变一个字符串，你可以创建一个新的字符串，其中包含修改后的字符
串即可。
· Java定义了一个和String类同等的类叫StringBuffer，它允许字符串改变，因此所有
正常的字符串操作在Java中还是可用的。
String类包含许多操作字符串的方法。例如下面就是其中一些。你可以用equals()来检
验两个字符串是否相等。你可以调用方法length()来获得一个字符串的长度。你可以调用
charAt()来获得一个字符串指定索引的字符。这三个方法的通用格式如下所示：
boolean equals(String object)
int length( )
char charAt(int index)

12. 使用命令行参数
注意：所有的命令行参数都是以字符串的形式传递的。你必须手工把数字值变换到它们的内部形式。
不过第一个参数不是当前的文件名，而是传入的参数。
 */


class Overload {
    // 7.1
    double width;
    double height;
    double depth;

    // 重载
    static void test(int a) {
        System.out.println("This is a: " + a);
    }

    static void test(int a, int b) {
        System.out.println("This is a, b: " + a + " " + b);
    }

    // 重载构造函数
    Overload() {
        width = 10;
        height = 10;
        depth = 10;
    }

    Overload(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    void show() {
        System.out.println(this.width * this.height * this.depth);
    }
}


class Recursion {
    // 7.5
    // 斐波拉契递归
    static int fact(int n) {
        int result;
        if (1 == n)
            return 1;
        result = fact(n - 1) * n;
        return result;
    }
}


class Stack {
    // 7.6访问控制
    private int stck[] = new int[10];
    private int tos;

    Stack() {
        this.tos = -1;
    }

    void push(int item) {
        if (9 == this.tos)
            System.out.println("Stack is full.");
        else
            stck[++this.tos] = item;
    }

    int pop() {
        if (this.tos < 0) {
            System.out.println("Stack underflow.");
            return 0;
        } else
            return stck[this.tos--];
    }
}


class UseStatic {
    // 7.7
    // 注意在一个static方法中引用任何实例变量都是非法的。
    static int a = 3;
    static int b;		// 未初始化的static变量被设置为当前类型的0值

    static void meth(int x) {
        System.out.println("x = " + x);
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

    static {
        // 静态模块
        System.out.println("Static block initialized.");
        System.out.println("Initialize b is: " + b);
        b = a * 4;
    }
}


class Stack2 {
    // 7.9
    // 生成任意长度的栈

    private int stack[];
    private int tos;

    Stack2(int size) {
        stack = new int[size];
        tos = -1;
    }

    void push(int item) {
        if (tos == stack.length - 1)
            System.out.println("Stack is full.");
        else
            stack[++tos] = item;
    }

    int pop() {
        if (tos < 0) {
            System.out.println("Stack underflow.");
            return 0;
        }
        return stack[tos--];
    }
}


class Outer {
    // 7.10
    int outer_x = 100;

    void test() {
        Inner inner = new Inner();
        inner.display();
    }

    class Inner {
        void display() {
            System.out.println("display: outer_x = " + outer_x);
        }
    }

    static class Inner2 {
        void display2() {
//			System.out.println("display2: outer_x = " + outer_x);
//             不能直接访问outer_x成员
        }
    }
}


class Outer2 {
    // 7.10
    // 我们强调嵌套类在它的外部的范围之内声明，但在程序块的范围之内定义内部类是可能的。
    // 例如，在由方法定义的块中，或设置在for循环体内部，也可以定义嵌套类。
    int outer_x = 100;

    void test() {
        for (int i = 0; i < 10; i++) {
            class Inner {
                void display() {
                    System.out.println("display: outer_x = " + outer_x);
                }
            }
            Inner inner = new Inner();
            inner.display();
        }
    }
	/*
	 * 尽管嵌套类在日常的大多数编程中不使用，但当处理applet（小应用程序）时是特别有帮
	 * 助的。后面还将继续介绍匿名内部内，它是一个没有名字的内部类。
	 */
}


class StringDemo {
    // 7.11
    static void stringTester() {
        String str1 = "First String";
        String str2 = "Second String";
        String str3 = str1;

        System.out.println("Length of str1: " + str1.length());
        System.out.println("Char at index 3 in str1: " + str1.charAt(3));

        if (str1.equals(str2))
            System.out.println("str1 == str2");
        else
            System.out.println("str1 != str2");

        if (str1.equals(str3))
            System.out.println("str1 == str3");
        else
            System.out.println("str1 != str3");
    }
}

public class chapter_07 {

    public static void main(String args[]) {
//        Overload.test(10);
//        Overload.test(10, 20);
//
//        Overload a = new Overload();
//        a.show();
//        Overload b = new Overload(10, 20, 30);
//        b.show();

//        System.out.println(Recursion.fact(3));
//        System.out.println(Recursion.fact(4));

//        Stack stk1 = new Stack();
//        Stack stk2 = new Stack();
//        for (int i = 0; i < 10; i++)
//            stk1.push(i);
//        for (int i = 10; i < 20; i++)
//            stk2.push(i);
//        System.out.println("stk1:");
//        for (int i = 0; i < 10; i++)
//            System.out.println(stk1.pop());
//        System.out.println("stk2:");
//        for (int i = 0; i < 10; i++)
//            System.out.println(stk2.pop());

        // 一旦UseStatic类被装载，所有的static语句被运行。首先，a被设置为3，接着
        // static块执行（打印一条消息），最后，b被初始化为a*4或12。然后调用main()。
//        UseStatic.meth(42);

        // 7.9
//        Stack2 stack1 = new Stack2(5);
//        Stack2 stack2 = new Stack2(8);
//        for (int i = 0; i < 5; i++) stack1.push(i);
//        for (int i = 0; i < 8; i++) stack2.push(i);
//        System.out.println("stack1: ");
//        for (int i = 0; i < 5; i++)
//            System.out.print(String.valueOf(stack1.pop()) + ' ');
//        System.out.println("\nstack2: ");
//        for (int i = 0; i < 8; i++)
//            System.out.print(String.valueOf(stack2.pop()) + ' ');

        // 7.10
//        Outer outer = new Outer();
//        outer.test();
//        Outer2 outer = new Outer2();
//        outer.test();

        // 7.11
        StringDemo.stringTester();
    }
}
